package edu.unc.ils.harvest;

import java.io.ByteArrayInputStream;

import java.io.File;
import java.io.FileWriter;


import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.jzkit.search.provider.iface.IRQuery;
import org.jzkit.search.provider.iface.Searchable;
import org.jzkit.search.provider.z3950.Z3950ServiceFactory;
import org.jzkit.search.util.RecordModel.ArchetypeRecordFormatSpecification;
import org.jzkit.search.util.RecordModel.marc.iso2709;
import org.jzkit.search.util.ResultSet.IRResultSet;
import org.jzkit.search.util.ResultSet.IRResultSetStatus;
import org.jzkit.z3950.QueryModel.Type1QueryModelBuilder;
import org.jzkit.z3950.QueryModel.Z3950QueryModel;
import org.marc4j.marcxml.Converter;
import org.marc4j.marcxml.MarcXmlReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class Z3950Client
{
	ApplicationContext appContext = 
		new ClassPathXmlApplicationContext( "Z3950ApplicationContext.xml" );
	
	Z3950ServiceFactory factory = null;
	
	File outputDir = null;
	public void setOutputDirectory(File dir)
	{
		outputDir = dir;
		if (!outputDir.exists())
			outputDir.mkdirs();
	}
	public void connect(String host, int port) 
	{
	    if ( appContext == null ) 
	      throw new RuntimeException("Unable to locate TestApplicationContext.xml definition file");

	    //String host = "agricola.nal.usda.gov"; // z3950.loc.gov
	    //int port = 7190;// 7090
	    factory = new Z3950ServiceFactory(host, port);
	    factory.setApplicationContext(appContext);
	    factory.setDefaultRecordSyntax("usmarc");
	    factory.setDefaultElementSetName("F");
	    Map<String, String> archetypes = (Map<String, String>)factory.getRecordArchetypes();
	    archetypes.put("Default","usmarc::F");
	    archetypes.put("FullDisplay","usmarc::F");
	    archetypes.put("BriefDisplay","usmarc::B");
	    archetypes.put("Holdings","usmarc::F");
	}
	
	public void search(String prefixString, String title, String issn) throws Exception 
	{
	    IRQuery query = new IRQuery();
	    query.collections = new Vector<String>();
	    query.collections.add("Voyager");
	    //query.query = new org.jzkit.search.util.QueryModel.PrefixString.PrefixString("@attrset bib-1 @attr 1=4 \"brain\"");
	    //query.query = new org.jzkit.search.util.QueryModel.PrefixString.PrefixString("@attrset bib-1 @attr 1=1016 \"Plant pathology journal\"");

	    
	    query.query = new org.jzkit.search.util.QueryModel.PrefixString.PrefixString(prefixString);
	    
	    Z3950QueryModel zqm = Type1QueryModelBuilder.buildFrom(appContext, query.query, "utf-8");
	    Searchable s = factory.newSearchable();
	    s.setApplicationContext(appContext);

	    
	    IRResultSet result = s.evaluate(query);

	    result.waitForStatus(IRResultSetStatus.COMPLETE|IRResultSetStatus.FAILURE,0);

	    //int status = result.getStatus();
	    //int count = result.getFragmentCount();

	    Enumeration e = new org.jzkit.search.util.ResultSet.ReadAheadEnumeration(result, 
	    		new ArchetypeRecordFormatSpecification("Default"));

		MarcXmlReader producer = new MarcXmlReader();
		Converter converter = new Converter();
		
	   // for ( int i=0; ( ( e.hasMoreElements() ) && ( i < 10 ) ); i++) 
		int i=0;
		while (e.hasMoreElements())
	    {
			System.err.println("Processing result "+i);
			iso2709 o = (iso2709)e.nextElement();
			StringWriter sw = new StringWriter();
			InputSource in = new InputSource(new ByteArrayInputStream((byte[])o.getOriginalObject()));
			Source source = new SAXSource(producer, in);
			Result r = new StreamResult(sw);
			converter.convert(source, r);
			
			String xml = sw.getBuffer().toString();
			xml = xml.replace("&#27;", " ");
			xml = xml.replace("&#30;", " ");
			xml = xml.replace("&#31;", " ");
			

			try
			{
	            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	            DocumentBuilder db = dbf.newDocumentBuilder();
	            Document doc = db.parse(new ByteArrayInputStream(xml.getBytes()));
			    XPathFactory  factory=XPathFactory.newInstance();
			    XPath xPath=factory.newXPath();
			
			    //System.out.println(xml);
			    NodeList nodeList = (NodeList)xPath.evaluate("/collection/record/controlfield[@tag='001']",
			            doc, XPathConstants.NODESET);
			   
			    String id = "";
			    for (int j=0; j<nodeList.getLength(); j++)
			    {
			            Node node = nodeList.item(j);
			            id = node.getNodeValue();
			    }
			    
			    nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='773']/subfield[@code='t']",
			            doc, XPathConstants.NODESET);
			   
			    String journalTitle = "";
			    for (int j=0; j<nodeList.getLength(); j++)
			    {
			            Node node = nodeList.item(j);
			            journalTitle = node.getNodeValue();
			    }
			    
			    
			    nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='022']/subfield",
			            doc, XPathConstants.NODESET);
			    List<String> issns = new ArrayList<String>();
			    for (int j=0; j<nodeList.getLength(); j++)
			    {
			            Node node = nodeList.item(j);
			            String is = node.getNodeValue();
			            issns.add(is);
			    }
			    
			    nodeList = (NodeList)xPath.evaluate("/collection/record/datafield[@tag='773']/subfield[@code='t']",
			            doc, XPathConstants.NODESET);
	
			    journalTitle = journalTitle.replaceAll("\\.", "");
			    journalTitle = journalTitle.replaceAll("&amp;", "&");
			    if ( (title != null && journalTitle.equals(title)) || 
			    		(issn != null && issns.contains(issn)))
			    {
				    File outputFile = new File(outputDir.getAbsolutePath() + File.separator + id + ".xml");
				    FileWriter fw = new FileWriter(outputFile);
				    fw.write(xml);
				    fw.close();
			    } else {
			    	System.out.println(journalTitle + " does not match " + title);
			    }
		    	// 001 = control number
		    	// 100 $a = main entry personal name
		    	// 245 $a = title
		    	// 520 $a = abstract
		    	// 650 $a = topical 
		    	// 651 $a = geographic
		    	// 653 $a = uncontrolled
		    	// 655 $a = form/genre
		    	// 700 $a = added entry personal name
		    	// 773 $t = journal title
		    	// 773 $t = journal title
			} catch (Exception e2) {
				e2.printStackTrace();
			}
			i++;
	    }
	    s.close();
	}
}
